package com.csx.base.concurrency.aqs.share;

import com.csx.base.concurrency.util.TimeUtil;

import java.util.concurrent.*;

/**
 * <p> what does this class do ? </p>
 *   该案例为 Semaphore 的使用案例
 *   描述:
 *      这里模拟的是一个场景: 假设有一些MySQL IO任务需要执行，但是这些任务不是核心任务，为了不影响主要业务的执行，只能从线程池中抽出3个线程同时执行
 *      任务，在这种场景下就可以使用 Semaphore 信号量来处理
 *      这里向线程池提交100个 MySQL IO任务，但是同时只有3个线程能够进入到MySQL IO任务的执行
 * <p> How does it work ?</p>
 * <p> Why does it do this ?</p>
 *
 * @author cuisongxu
 * @date 2024/12/4 周三 8:32
 */
public class SemaphoreTest {

    private final ExecutorService executor = new ThreadPoolExecutor(
            5,
            10,
            300,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            new ThreadPoolExecutor.AbortPolicy()
    );

    // 信号量,设置为3，也就是允许的并发值为3
    private final Semaphore semaphore = new Semaphore(3);


    public static void main(String[] args) {
        SemaphoreTest semaphoreTest = new SemaphoreTest();
        semaphoreTest.test();
    }
    public void test() {

        // 执行了100个 MySQL IO 任务
        for (int i = 0; i < 100; i++) {
            executor.submit(new MySQLIOTask(i));
        }

        // 等待线程池执行线程完毕
        // 备注: 在实际开发中不能贸然关闭线程池
        executor.shutdown();
        while (!executor.isTerminated()) {
            try {
                TimeUnit.MILLISECONDS.sleep(20);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        System.out.println("main end");
    }


    class MySQLIOTask implements Runnable {

        private final int taskId;

        public MySQLIOTask(int taskId) {
            this.taskId = taskId;
        }

        @Override
        public void run() {
            try {
                // 获取执行许可
                // 这里由于设置了 semaphore 的许可为3，也就是在多线程并发执行 MySQLIOTask
                // 时，最多只有3个线程能同时执行 MySQL IO 任务
                semaphore.acquire();
                // 模拟执行 MySQL IO 任务的耗时
                TimeUnit.SECONDS.sleep(1);
                String nowTimeStr = TimeUtil.getNowTimeStr();
                System.out.println(nowTimeStr + " " + Thread.currentThread().getName() + " 执行了MySQL IO 任务，任务编号为: " + taskId);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }finally {
                // 释放许可
                semaphore.release();
            }
        }
    }
}
